/* 
 * File:   Arquivo.cpp
 * Author: Pedro
 * 
 * Created on 13 de Agosto de 2010, 08:56
 */

#include "Arquivo.h"
#include "Lexema.h"
#include "../TiposDeLexema.h"
#include "Identificador.h"
#include "ConstanteString.h"
#include "ConstanteNumerica.h"
#include "PalavraReservada.h"
using namespace Texto;

//inicialização da variavel estatica que conta a linha do arquivo.

//construtor da classe
Arquivo::Arquivo(string nome)
{
    this->NumLinha = 1;
    this->nome = nome;   
}

Arquivo::Arquivo(const Arquivo& orig)
{
    this->nome = orig.nome;
    this->NumLinha = orig.NumLinha;
}

//destrutor da classe
Arquivo::~Arquivo()
{
    this->handler.close();
}

//retorna o nome do arquivo
string Arquivo::getNomeArquivo()
{
    return this->nome;
}

//abre o arquivo
bool Arquivo::AbrirArquivo()
{
    this->handler.open(this->nome.c_str(), ifstream::in);

    if (!(this->handler.is_open()))
        return false;
    else
        return true;
}

//le um caracter do arquivo --> Conta a linha de leitura
char Arquivo::lerCaracter()
{
    char c;
    c = this->handler.get();
    if(c=='\n'){
        NumLinha++;
    }
    return c;
}

// le uma linha inteira do arquivo
string Arquivo::lerLinha()
{
    string retorno;
    char buffer[4096];
    this->handler.getline(buffer, 4096);
    retorno.assign(buffer);
    NumLinha++;
    return retorno;
}

/**Le o proximo lexema do arquivo
 *  Retorna um ponteiro para lexema
 *  este ponteiro pode assumir a forma de qualquer subclasse da classe Lexema
 *  para que seja feita a verificação de validação ultilizando polimorfismo.
 */
Lexema* Arquivo::RetornaProximoLexema()
{

    Lexema *lexema;
    string retorno = "";
    char lido;
    bool constante = false;
    bool continuaExec = false;

    while (!this->handler.eof() && (lido = this->lerCaracter()) == ' ' || lido == '\n' || lido == '\r' || lido == '\t');

    if (this->handler.eof())
    {
         lexema = new Lexema();
         lexema->assign("<EOF>");
         return lexema;
    }

    retorno += lido;

    if(Lexema::IsSimbolo(lido))
    {
        string auxiliar;
        char lido2 = this->lerCaracter();
        auxiliar += lido;
        auxiliar += lido2;
        if (Lexema::IsSimboloDuplo(lido) && Lexema::DeterminaSimboloDuplo(auxiliar))
           retorno += lido2;
        else
            this->handler.seekg(-1,ios_base::cur);

        PalavraReservada  *palavra;
        palavra  = new PalavraReservada();
        palavra->assign(retorno);

        return palavra;
    } else if(lido == '"')
    {
        constante = true;
    }

    do{
        if(continuaExec)
           retorno += lido;
     
        continuaExec = false;

        if(!constante){
        while ((((lido = this->lerCaracter()) != ' ')
                && lido != '\n'
                && lido != '\r'
                && lido != '\t'
                && !this->handler.eof()
                && (!Lexema::IsSimbolo(lido))))
                retorno += lido;

          if(lido == '-' && (Lexema::IsLetra(lido = this->lerCaracter())) || (Lexema::IsNumero(lido)) || lido == '-' )
          {
              retorno += '-';
              continuaExec = true;
          }

          }else{

              while(((lido = this->lerCaracter())!='"') && lido!='\n')
                  retorno += lido;

              retorno += lido;
          }

        }while(continuaExec);

       if (!Lexema::IsLetra(lido) && lido != '"')
       {
           this->handler.seekg(-1,ios_base::cur);
       }
    switch(Lexema::retornaTipo(retorno))
    {
        case _Identificador:
            lexema = new Identificador();
        break;
        case Constante_string:
            lexema = new ConstanteString();
        break;
        case Constante_Numerica:
            lexema = new ConstanteNumerica();
            break;
        case Palavra_reservada:
            lexema = new PalavraReservada();
            break;
        default:
            lexema = new Lexema();
        break;
    }
    lexema->assign(retorno);
    return lexema;
}

//verifica se é o fim do arquivo
bool Arquivo::fimDoArquivo()
{
    return this->handler.eof();
}

//retorna a linha atual
int Arquivo::getNumLinha()
{
    return this->NumLinha;
}
